using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using GraphAlgsPresentation.Core;
using GraphAlgsPresentation.ScriptLibrary;
using System.Diagnostics;
using System.Drawing.Drawing2D;

namespace GraphAlgsPresentation.ScriptBuilder
{
    /// <summary>
    /// Provides a library for building the script
    /// </summary>
    public static class ScriptBuilder
    {
        private static Script _script = new Script();
        private static List<Command> currentStep = new List<Command>();

        /// <summary>
        /// Gets the current visual-script.
        /// </summary>
        /// <value>The current.</value>
        private static VisualScript Current
        {
            get
            {
                VisualScript tmp = new VisualScript();
                StringBuilder s = new StringBuilder();
                _script.Context.LayoutContext.Serialize(new StringWriter(s));
                tmp.LayoutContext = LayoutContext.Deserialize(new StringReader(s.ToString()));
                tmp.Commands = new List<List<Command>>();
                foreach (List<Command> lstCmd in _script.Commands)
                    tmp.Commands.Add(lstCmd);
                tmp.Commands.Add(currentStep);
                return tmp;
            }
        }

        /// <summary>
        /// Change_s the edge style.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="style">The style.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeStyle(string v1, string v2, DashStyle style, DrawEffect effect)
        {
            ChangeEdgeStyle cmd = new ChangeEdgeStyle(v1, v2, style, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the edge style.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="style">The style.</param>
        public static void Change_EdgeStyle(string v1, string v2, DashStyle style)
        {
            Change_EdgeStyle(v1, v2, style, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the edge style.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="style">The style.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeStyle(int v1, int v2, DashStyle style, DrawEffect effect)
        {
            ChangeEdgeStyle cmd = new ChangeEdgeStyle(v1, v2, style, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the edge style.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="style">The style.</param>
        public static void Change_EdgeStyle(int v1, int v2, DashStyle style)
        {
            Change_EdgeStyle(v1, v2, style, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="label">The label.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeLabel(string v1, string v2, string label, DrawEffect effect)
        {
            ChangeEdgeLabel cmd = new ChangeEdgeLabel(v1, v2, label, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="label">The label.</param>
        public static void Change_EdgeLabel(string v1, string v2, string label)
        {
            Change_EdgeLabel(v1, v2, label, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="NewLabel">The new label.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeLabel(int v1, int v2, string NewLabel, DrawEffect effect)
        {
            Command cmd = new ChangeEdgeLabel(v1, v2, NewLabel, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="NewLabel">The new label.</param>
        public static void Change_EdgeLabel(int v1, int v2, string NewLabel)
        {
            Change_EdgeLabel(v1, v2, NewLabel, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the edge line.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeLineColor(string v1, string v2, Color color, DrawEffect effect)
        {
            ChangeEdgeLineColor cmd = new ChangeEdgeLineColor(v1, v2, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the edge line.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="color">The color.</param>
        public static void Change_EdgeLineColor(string v1, string v2, Color color)
        {
            Change_EdgeLineColor(v1, v2, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the edge line.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="NewColor">The new color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeLineColor(int v1, int v2, Color NewColor, DrawEffect effect)
        {
            ChangeEdgeLineColor cmd = new ChangeEdgeLineColor(v1, v2, NewColor, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the edge line.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="NewColor">The new color.</param>
        public static void Change_EdgeLineColor(int v1, int v2, Color NewColor)
        {
            Change_EdgeLineColor(v1, v2, NewColor, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeLabelColor(string v1, string v2, Color color, DrawEffect effect)
        {
            ChangeEdgeLabelColor cmd = new ChangeEdgeLabelColor(v1, v2, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's name</param>
        /// <param name="v2">Destination vertex's name</param>
        /// <param name="color">The color.</param>
        public static void Change_EdgeLabelColor(string v1, string v2, Color color)
        {
            Change_EdgeLabelColor(v1, v2, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_EdgeLabelColor(int v1, int v2, Color color, DrawEffect effect)
        {
            ChangeEdgeLabelColor cmd = new ChangeEdgeLabelColor(v1, v2, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the edge label.
        /// </summary>
        /// <param name="v1">Source vertex's id</param>
        /// <param name="v2">Destination vertex's id</param>
        /// <param name="color">The color.</param>
        public static void Change_EdgeLabelColor(int v1, int v2, Color color)
        {
            Change_EdgeLabelColor(v1, v2, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the vertex label.
        /// </summary>
        /// <param name="_name">Vertex's name</param>
        /// <param name="label">The label.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VertexLabel(string name, string label, DrawEffect effect)
        {
            ChangeVertexLabel cmd = new ChangeVertexLabel(name, label, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the vertex label.
        /// </summary>
        /// <param name="_name">Vertex's name</param>
        /// <param name="label">The label.</param>
        public static void Change_VertexLabel(string name, string label)
        {
            Change_VertexLabel(name, label, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the vertex label.
        /// </summary>
        /// <param name="id">The vertex's id.</param>
        /// <param name="label">The label.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VertexLabel(int id, string label, DrawEffect effect)
        {
            ChangeVertexLabel cmd = new ChangeVertexLabel(id, label, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the vertex label.
        /// </summary>
        /// <param name="id">The vertex's id.</param>
        /// <param name="label">The label.</param>
        public static void Change_VertexLabel(int id, string label)
        {
            Change_VertexLabel(id, label, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the vertex fill.
        /// </summary>
        /// <param name="_name">Vertex's name</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VertexFillColor(string name, Color color, DrawEffect effect)
        {
            ChangeVertexFillColor cmd = new ChangeVertexFillColor(name, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the vertices fill.
        /// </summary>
        /// <param name="vertices">The vertices.</param>
        /// <param name="colors">The colors.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VerticesFillColor(IEnumerable<int> vertices, List<Color> colors, DrawEffect effect)
        {
            List<string> xmlColors = new List<string>();
            foreach (Color color in colors) xmlColors.Add(XmlDataConverter.SerializeColor(color));
            ChangeVerticesFillColor cmd = new ChangeVerticesFillColor(vertices, xmlColors, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the vertex fill.
        /// </summary>
        /// <param name="_name">Vertex's name</param>
        /// <param name="color">The color.</param>
        public static void Change_VertexFillColor(string name, Color color)
        {
            Change_VertexFillColor(name, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the vertex fill.
        /// </summary>
        /// <param name="id">The vertex's id.</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VertexFillColor(int id, Color color, DrawEffect effect)
        {
            ChangeVertexFillColor cmd = new ChangeVertexFillColor(id, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the vertex fill.
        /// </summary>
        /// <param name="id">The vertex's id.</param>
        /// <param name="color">The color.</param>
        public static void Change_VertexFillColor(int id, Color color)
        {
            Change_VertexFillColor(id, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the vertex text.
        /// </summary>
        /// <param name="_name">Vertex's name</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VertexTextColor(string name, Color color, DrawEffect effect)
        {
            ChangeVertexTextColor cmd = new ChangeVertexTextColor(name, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the vertex text.
        /// </summary>
        /// <param name="_name">Vertex's name</param>
        /// <param name="color">The color.</param>
        public static void Change_VertexTextColor(string name, Color color)
        {
            Change_VertexTextColor(name, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Change_s the color of the vertex text.
        /// </summary>
        /// <param name="id">The vertex's id.</param>
        /// <param name="color">The color.</param>
        /// <param name="effect">The effect.</param>
        public static void Change_VertexTextColor(int id, Color color, DrawEffect effect)
        {
            ChangeVertexTextColor cmd = new ChangeVertexTextColor(id, color, effect);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Change_s the color of the vertex text.
        /// </summary>
        /// <param name="id">The vertex's id.</param>
        /// <param name="color">The color.</param>
        public static void Change_VertexTextColor(int id, Color color)
        {
            Change_VertexTextColor(id, color, DrawEffect.NoEffect);
        }

        /// <summary>
        /// Delays the specified time out.
        /// </summary>
        /// <param name="TimeOut">The time out.</param>
        public static void Delay(int TimeOut)
        {
            Command cmd = new Delay(TimeOut);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Sets the text.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="text">The text.</param>
        public static void SetText(int index, string text)
        {
            Command cmd = new SetText(index, text);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Inserts the text.
        /// </summary>
        /// <param name="text">The text.</param>
        public static void InsertText(string text)
        {
            Command cmd = new InsertText(text);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Changes the color of the text.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="color">The color.</param>
        public static void ChangeTextColor(int index, Color color)
        {
            Command cmd = new ChangeTextColor(index, color);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Changes the text font.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="font">The font.</param>
        public static void ChangeTextFont(int index, Font font)
        {
            Command cmd = new ChangeTextFont(index, font);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Resets the line.
        /// </summary>
        /// <param name="index">The index.</param>
        public static void ResetLine(int index)
        {
            Command cmd = new ResetLine(index);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Highlights the line.
        /// </summary>
        /// <param name="index">The index.</param>
        public static void HighlightLine(int index)
        {
            Command cmd = new HighlightLine(index);
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Clears the text.
        /// </summary>
        public static void ClearText()
        {
            Command cmd = new ClearText();
            currentStep.Add(cmd);
        }

        /// <summary>
        /// Read the graph in the XML file create by GraphEditor.
        /// </summary>
        /// <param name="InputFileName">Path to the input file.</param>
        /// <returns>The graph represented by a two-dimension array, double.NaN means no edge.</returns>
        public static double[,] GetGraph(string InputFileName)
        {
            // Read file
            TextReader reader = new StreamReader(InputFileName);
            LayoutContext LCContext = LayoutContext.Deserialize(reader);
            reader.Close();

            _script = new Script();
            _script.Context.LayoutContext = LCContext;

            int n = LCContext.Vertices.Count;   // Graph size
            double[,] result = new double[n, n];

            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    result[i, j] = double.NaN;

            foreach (LCEdge e in LCContext.Edges)
            {
                int i = Convert.ToInt32(e.Source.Name) - 1;
                int j = Convert.ToInt32(e.Destination.Name) - 1;
                result[i, j] = e.Weight;
                if (!e.Directed)    // Undirected edge
                    result[j, i] = e.Weight;
            }

            return result;
        }

        /// <summary>
        /// End the current step of the script.
        /// </summary>
        public static void EndStep()
        {
            _script.Commands.Add(currentStep);
            currentStep = new List<Command>();
        }

        /// <summary>
        /// End script and write script to output file.
        /// </summary>
        /// <param name="OutputFileName">Output file path.</param>
        public static void Finish(string OutputFileName)
        {
            if (currentStep.Count > 0)
                EndStep();
            TextWriter writer = new StreamWriter(OutputFileName);
            _script.Serialize(writer);
            writer.Close();
        }
    }
}
